/**
 * MINAPL Code generator.
 * Copyright 2012 Michael A. Muller
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */

#include "CodeGen.h"

#include <assert.h>

using namespace minapl;

void CodeGen::loadAddr(void *addr) {

    // mov #addr, %eax
    emitByte(0xB8);
    emitWord(addr);
}

void CodeGen::loadZero() {
    // xor %eax, %eax
    emitByte(0x31);
    emitByte(0xc0);
}

void CodeGen::pushLast() {
    // push %eax
    emitByte(0x50);
}

void CodeGen::emitFuncCall(void *addr) {
    emitByte(0xE8);
    emitOffset(addr);
}

void CodeGen::emitFuncEnter() {
    // push %ebp
    emitByte(0x55);

    // mov %esp, %ebp
    emitByte(0x89);
    emitByte(0xE5);
}

void CodeGen::emitReturn() {
    // leave
    emitByte(0xC9);

    // ret
    emitByte(0xC3);
}


void CodeGen::emitLocalVarRef(int index) {
    index *= sizeof(void *);
    // mov index(%ebp), %eax
    emitByte(0x8B);
    if (index > 127 || index < -128) {
        // 32 bit index
        emitByte(0x85);
        emitWord((void *)index);
    } else {
        // 8 bit index
        emitByte(0x45);
        emitByte(index);
    }
}

void CodeGen::emitLocalVarStore(int index) {
    index *= sizeof(void *);

    // mov %eax, index(%ebp)
    emitByte(0x89);
    if (index > 127 || index < -128) {
        // 32 bit index
        emitByte(0x85);
        emitWord((void *)index);
    } else {
        // 8 bit index
        emitByte(0x45);
        emitByte(index);
    }
}

void CodeGen::emitLoadLocalVarAddr(int index) {
    index *= sizeof(void *);

    emitByte(0x8D);
    if (index > 127 || index < -128) {
        // 32 bit index
        emitByte(0x85);
        emitWord((void *)index);
    } else {
        // 8 bit index
        emitByte(0x45);
        emitByte(index);
    }
}

void CodeGen::emitSPAdd(ssize_t amt) {
    // for a value of zero, this is a no-op
    if (!amt) return;

    // add $1, %esp
    if (amt > 127 || amt < -128) {
        emitByte(0x81);
        emitByte(0xC4);
        emitWord((void *)amt);
    } else {
        emitByte(0x83);
        emitByte(0xC4);
        emitByte(amt);
    }
}

void CodeGen::emitLoadSP() {
    // mov %esp, %ebp
    emitByte(0x89);
    emitByte(0xE0);
}

void CodeGen::emitStoreArg(int index, int hint) {
    switch (hint) {
        case HINT_ADD:
        case HINT_SUB:
        case HINT_MUL:
        case HINT_DIV:
        case HINT_EQ:
            assert(index == 1);
            pushLast();
            break;
        case HINT_CALL:
            pushLast();
            break;
        default:
            assert(0 && "Invalid hint type");
    }
}

namespace {
    void emitZeroFlag(CodeGen &cg) {
        // lahf ; flags -> AH
        cg.emitByte(0x9F);

        // and %eax, 0x4000 ; isolate the "zero" flag
        cg.emitByte(0x25);
        cg.emitWord((void *)0x4000);
    }

    void emitNegateZeroFlag(CodeGen &cg) {
        // isolate and negate the zero flag
        // mov $0x40, %eax
        cg.emitByte(0xB8);
        cg.emitWord((void *)0x40);

        // lahf ; flags -> AH
        cg.emitByte(0x9F);

        // and %al, %ah
        cg.emitByte(0x20);
        cg.emitByte(0xC4);

        // xor %al, %ah
        cg.emitByte(0x30);
        cg.emitByte(0xC4);

        // shr $14, %ax
        cg.emitByte(0x66);
        cg.emitByte(0xC1);
        cg.emitByte(0xE8);
        cg.emitByte(0x0E);
    }
}

/**
 * Emit a test for equality on accum, op2.  This will store 1 in the
 * accumulator if the operands are equal, zero if not.
 */
void CodeGen::emitEqTest() {
    // pop %ebx
    emitByte(0x5B);

    // sub %eax, %ebx
    emitByte(0x29);
    emitByte(0xC3);
    emitZeroFlag(*this);
}

/**
 * Emit a test for "not equals."
 */
void CodeGen::emitNEqTest() {
    // pop %ebx
    emitByte(0x5B);

    // sub %eax, %ebx
    emitByte(0x29);
    emitByte(0xC3);
    emitNegateZeroFlag(*this);
}

/**
 * Emit "logical not" instruction.
 */
void CodeGen::emitNot() {
    // test %eax, %eax
    emitByte(0x85);
    emitByte(0xC0);

    emitZeroFlag(*this);
}

void CodeGen::emitSub() {
    // pop %ebx
    emitByte(0x5B);

    // sub %ebx, %eax ; %eax = %eax - %eax
    emitByte(0x29);
    emitByte(0xD8);
}

void CodeGen::emitAdd() {
    // pop %ebx
    emitByte(0x5B);

    // addl %ebx, %eax
    emitByte(0x01);
    emitByte(0xD8);
}

void CodeGen::emitMul() {
    // pop %ebx
    emitByte(0x5B);

    // imul %ebx, %eax ; %eax = %eax * %ebx
    emitByte(0x0F);
    emitByte(0xAF);
    emitByte(0xC3);
}

void CodeGen::emitDiv() {
    // pop %ebx
    emitByte(0x5B);

    // xor %edx, %edx
    emitByte(0x31);
    emitByte(0xD2);

    // idiv %ebx ; result in %eax, remainder in %edx
    emitByte(0xF7);
    emitByte(0xFB);
}


/**
 * Emit a jump to the specified address.
 */
void CodeGen::emitJump(void *addr) {
    // jmp address
    emitByte(0xE9);
    emitOffset(addr);
}

/**
 * Emit a jump to an incomplete address.  Returns a pointer that can be
 * used to fix the instruction (see fixAddr()) once the address is known.
 */
void **CodeGen::emitIncompleteJump() {
    // jmp 0
    emitByte(0xE9);
    void **result = (void **)curCode;
    emitWord(0);
    return result;
}

/**
 * Emit a "jump if zero" to an incomplete address.  See
 * emitIncompleteJump(), same rules apply.
 */
void **CodeGen::emitIncompleteJZ() {
    // test %eax, %eax
    emitByte(0x85);
    emitByte(0xC0);

    // jz 0
    emitByte(0x0F);
    emitByte(0x84);
    void **result = (void **)curCode;
    emitWord(0);
    return result;
}

/**
 * Emit a call to an incomplete address.  See emitIncompleteJump(), same
 * rules apply.
 */
void **CodeGen::emitIncompleteCall() {
    // call 0
    emitByte(0xE8);
    void **result = (void **)curCode;
    emitWord(0);
    return result;
}

/**
 * Fix a call site address returned by emitIncompleteJump() or
 * emitIncompleteCall().
 */
void CodeGen::fixAddr(void **callSite, void *addr) {
    *callSite = (void *)( (uint8_t *)addr - (uint8_t *)(callSite + 1) );
}

/**
 * Load the word at the address referenced by the accumulator into the
 * accumulator. ("p = *p" in C)
 */
void CodeGen::emitLoad() {
    // movl %eax, %esi
    emitByte(0x89);
    emitByte(0xC6);

    // movl (%esi), %eax
    emitByte(0x8B);
    emitByte(0x06);
}

